/*
 *  Copyright (c) 2010 Brian Stanek <bstanek@gmail.com>
 *
 *  Permission is hereby granted, free of charge, to any person
 *  obtaining a copy of this software and associated documentation
 *  files (the "Software"), to deal in the Software without
 *  restriction, including without limitation the rights to use,
 *  copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following
 *  conditions:
 *
 *  The above copyright notice and this permission notice shall be
 *  included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *  OTHER DEALINGS IN THE SOFTWARE.
 */

package guice.binder
{

import guice.Binder;
import guice.Key;
import guice.Scope;
import guice.binding.AbstractBinding;
import guice.binding.UntargettedBindingImpl;
import guice.scopes.Scoping;
import guice.spi.Element;
import guice.spi.InstanceBinding;
import guice.spi.UntargettedBinding;

public class AbstractBindingBuilder
{

    //--------------------------------------------------------------------------
    //
    //  Class Constants
    //
    //--------------------------------------------------------------------------

    public static const IMPLEMENTATION_ALREADY_SET:String = "Implementation is set more than once.";

    public static const SINGLE_INSTANCE_AND_SCOPE:String = "Setting the scope is not permitted when binding to a single instance.";

    public static const SCOPE_ALREADY_SET:String = "Scope is set more than once.";

    public static const BINDING_TO_NULL:String = "Binding to null instances is not allowed.";

    public static const CONSTANT_VALUE_ALREADY_SET:String = "Constant value is set more than once.";

    public static const ANNOTATION_ALREADY_SPECIFIED:String = "More than one annotation is specified for this binding.";

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    public function AbstractBindingBuilder(binder:Binder,
                                           elements:Vector.<Element>,
                                           source:Object, key:Key)
    {
        super();
        this.binder = binder;
        this.elements = elements;
        this.source = source;
        this.key = key;
        binding = new UntargettedBindingImpl(source, key, Scoping.UNSCOPED);
        elements.push(binding);
    }

    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------

    protected var binder:Binder;

    private var binding:AbstractBinding;

    private var elements:Vector.<Element>;

    private var source:Object;

    private var key:Key;

    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------

    protected function getBinding():AbstractBinding
    {
        return binding;
    }

    protected function setBinding(binding:AbstractBinding):AbstractBinding
    {
        var index:int = elements.indexOf(this.binding);
        elements.splice(index, 1, binding);
        this.binding = binding;
        return binding;
    }

    protected function annotatedWithInternal(annotation:String):AbstractBinding
    {
        checkNotAnnotated();
        return setBinding(binding.withKey(new Key(binding.getKey().getType(),
                annotation)));
    }

    public function inScope(scope:Scope):AbstractBinding
    {
        checkNotScoped();
        return setBinding(binding.withScoping(Scoping.forInstance(scope)));
    }

    public function asEagerSingleton():void
    {
        checkNotScoped();
        setBinding(binding.withScoping(Scoping.EAGER_SINGLETON));
    }

    protected function keyTypeIsSet():Boolean
    {
        return binding.getKey().getType() != null;
    }

    protected function checkNotTargetted():void
    {
        if (!(binding is UntargettedBinding))
        {
            binder.addError(IMPLEMENTATION_ALREADY_SET);
        }
    }

    protected function checkNotAnnotated():void
    {
        if (binding.getKey().getAnnotation())
        {
            binder.addError(ANNOTATION_ALREADY_SPECIFIED);
        }
    }

    protected function checkNotScoped():void
    {
        // scoping isn't allowed when we have only one instance
        if (binding is InstanceBinding)
        {
            binder.addError(SINGLE_INSTANCE_AND_SCOPE);
        }
        else if (binding.getScoping().isExplicitlyScoped())
        {
            binder.addError(SCOPE_ALREADY_SET);
        }
    }

}

}
